package cn.dengta.context.auth;

import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import javax.annotation.Nonnull;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import cn.dengta.common.context.RequestContext;
import cn.dengta.common.context.RequestUriResolver;
import cn.dengta.common.web.ServletUtil;
import me.codeplayer.util.StringUtil;
import org.springframework.http.HttpHeaders;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

public class PermissionInterceptor implements HandlerInterceptor {

	/** 权限定位符数组在request中的KEY值 */
	public static final String PERMISSION_LOCATOR_KEY = "permissionLocator";
	//
	protected PermissionPolicy permissionPolicy = new DefaultPermissionPolicy();
	/** CSRF 白名单：存储以 "/"开头的路径（不包含 contextPath） */
	protected final Set<String> whitePathList = new HashSet<>();

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
		if (handler instanceof HandlerMethod) {
			/* 先关闭同源安全监测
			if (!Env.inLocal() && tryDenyCsrf(request, response)) {
				return false;
			}
			*/
			final HandlerMethod handlerMethod = (HandlerMethod) handler;
			final PermissionLocator locator = permissionPolicy.parsePermission(request, handlerMethod.getBeanType(), handlerMethod.getMethod());
			if (locator != null) {
				final UserPermission sessionUser = RequestContext.getSessionUser(request);
				boolean allow = sessionUser != null && sessionUser.hasPermission(locator.getPermissionCode());
				if (!allow) {
					throw new PermissionException();
				}
				request.setAttribute(PERMISSION_LOCATOR_KEY, locator);
			}
		}
		return true;
	}

	public void setEntryPoints(String entryPoints) {
		String[] values = entryPoints.split(",");
		for (String value : values) {
			this.whitePathList.add(value.trim());
		}
	}

	/**
	 * 尝试防御 CSRF 攻击
	 */
	protected boolean tryDenyCsrf(HttpServletRequest request, HttpServletResponse response) {
		if (!"GET".equals(request.getMethod()) /* 非GET请求 */ && !inWhiteList(whitePathList, request) /* 不在白名单内 */) {
			String referer = ServletUtil.getReferer(request);
			if (StringUtil.isEmpty(referer)) {
				referer = request.getHeader(HttpHeaders.ORIGIN);
			}
			// 只允许同源 Referer 发出非 GET 请求
			boolean risky = StringUtil.isEmpty(referer);
			if (!risky) {
				int result = checkRefererRisk(referer, request);
				risky = result < 0 || result == 0 && !RequestUriResolver.isOwnURL(referer);
			}
			if (risky) {
				try {
					response.sendError(HttpServletResponse.SC_FORBIDDEN);
					return true;
				} catch (IOException e) {
					throw new IllegalStateException(e);
				}
			}
		}
		return false;
	}

	static Set<String> internalWhiteHostList = Set.of("servicewechat.com");

	protected int checkRefererRisk(@Nonnull String referer, HttpServletRequest request) {
		final String open = "://", close = "/";
		final int start = referer.indexOf(open);
		if (start != -1) {
			int fromIndex = start + open.length();
			final int end = referer.indexOf(close, fromIndex);
			String host = referer.substring(fromIndex, end == -1 ? referer.length() : end);
			if (host.equals(request.getServerName()) || internalWhiteHostList.contains(host)) {
				return 1; // 安全
			}
			return 0; // 未知
		}
		return -1; // 危险
	}

	protected boolean inWhiteList(@Nonnull Set<String> whiteList, HttpServletRequest request) {
		return !whiteList.isEmpty() && whiteList.contains(getRequestedPath(request));
	}

	protected String getRequestedPath(HttpServletRequest request) {
		String path = request.getServletPath();
		if (request.getPathInfo() != null) {
			path = path + request.getPathInfo();
		}
		return path;
	}

	public void setPermissionPolicy(PermissionPolicy permissionPolicy) {
		this.permissionPolicy = permissionPolicy;
	}

}
